﻿#include"XQFtp.hpp"
#include"curl/curl.h"
#include<QFileInfo>
int XQFtp::Upload()
{
    return Upload(m_local_file_path,m_remote_file_path);
}
// 上传进度
static int upProgressCallback(XQFtpFileTransferInfo* info, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
    XQFtpFileTransferInfo* LPinfo = info;
    LPinfo->curSize = ulnow;
    LPinfo->percent = (float)ulnow / ultotal;
    if (ulnow == 0)
        return 0;
    emit LPinfo->ftp->progress(*LPinfo);
    return 0;
}
// ----上传相关 ---- //
// 解析Content-Length头
size_t getContentLengthFunc(void* ptr, size_t size, size_t nmemb, void* stream)
{
    int r;
    long len = 0;

    r = sscanf((const char*)ptr, "Content-Length: %ld\n", &len);
    if (r) /* Microsoft: we don't read the specs */
        *((long*)stream) = len;
    return size * nmemb;
}
// 丢弃已经下载的数据
size_t discardFunc(void* ptr, size_t size, size_t nmemb, void* stream)
{
    return size * nmemb;
}
//读取数据上传
size_t readfunc(void* ptr, size_t size, size_t nmemb, void* stream)
{
    FILE* f = (FILE*)stream;
    size_t n;
    if (ferror(f))
        return CURL_READFUNC_ABORT;
    n = fread(ptr, size, nmemb, f) * size;
    return n;
}
int XQFtp::Upload(const QString& local_file_path, const QString& remote_file_path)
{
    if(!QFile(local_file_path).exists())
    {
        emit finish(XQFtpFileTransferInfo());
        return -1;
    }
    int tries = 3;
	curl_global_init(CURL_GLOBAL_ALL);//全局初始化
    CURL* curl=init();
    size_t fileSize = QFileInfo(local_file_path).size();//文件大小
    FILE* file=NULL;
    long uploaded_len = 0;
    CURLcode res = CURLE_GOT_NOTHING;
    file = fopen(local_file_path.toLocal8Bit().data(), "rb");
    if (file == NULL)
    {
        perror(NULL);
        return 0;
    }

    //上传使能
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    //设置url
    QString url = setUrl(remote_file_path);
    curl_easy_setopt(curl, CURLOPT_URL, setUrl(remote_file_path).toLocal8Bit().data());
   /* if (m_timeout)
        curl_easy_setopt(m_curl, CURLOPT_FTP_RESPONSE_TIMEOUT, m_timeout);*/
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, getContentLengthFunc);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, &uploaded_len);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, discardFunc);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, readfunc);
    curl_easy_setopt(curl, CURLOPT_READDATA, file);
    curl_easy_setopt(curl, CURLOPT_FTPPORT, "-"); /* disable passive mode */
    curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L);
    curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);//设置文件大小
    // 设置上传进度
    XQFtpFileTransferInfo info = { this,local_file_path ,remote_file_path,0,fileSize,0.0,tranType::up};
    emit start(info);
    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, upProgressCallback);
    curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &info);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);

    //    curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L); // 如果设置为1，调试模式将打印一些低级别的MSG

        // upload: 断点续传
    for (int c = 0; (res != CURLE_OK) && (c < tries); c++)
    {
        /* are we resuming? */
        if (c)
        { /* yes */
            /* determine the length of the file already written */
            /*
            * With NOBODY and NOHEADER, libcurl will issue a SIZE
            * command, but the only way to retrieve the result is
            * to parse the returned Content-Length header_recv. Thus,
            * getContentLengthfunc(). We need discardfunc() above
            * because HEADER will dump the headers to stdout
            * without it.
            */
            curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
            curl_easy_setopt(curl, CURLOPT_HEADER, 1L);
            res = curl_easy_perform(curl);
            if (res != CURLE_OK)
                continue;
            curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
            curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
            fseek(file, uploaded_len, SEEK_SET);
            curl_easy_setopt(curl, CURLOPT_APPEND, 1L);
        }
        else
            curl_easy_setopt(curl, CURLOPT_APPEND, 0L);

        res = curl_easy_perform(curl);
    }
    fclose(file);
    emit finish(std::move(info));
    int curl_state = 0;
    if (res == CURLE_OK)
        curl_state = 1;
    else
    {
        fprintf(stderr, "%s\n", curl_easy_strerror(res));
        curl_state = 0;
    }

    // exit m_curl handle
    curl_easy_cleanup(curl);
    curl_global_cleanup();

    return curl_state;
}